

#include "army.h"
#include "unit.h"
#include "gamemanager.h"
#include <algorithm>

Army::Army() :
frontLine(0)
, deaths(0)
, arrivals(0)
, idles(0)
, frontLinePosition(0.0f)
, frontLineIndex(0)
, faction(ArmyFaction::left) {
	memset(soldiers, 0, sizeof(Unit *) * TOTAL_LINES * ROWS_PER_LINE);
	memset(gridsState, 0, sizeof(int) * TOTAL_LINES * ROWS_PER_LINE);
	memset(positionTable, 0, sizeof(int) * TOTAL_LINES * ROWS_PER_LINE);

	memset(orders, 0, SQUAD_COUNT * sizeof(SquadOrder));
}

Army::~Army() {
	for (int i = 0; i < TOTAL_LINES; i++) {
		for (Unit *unit : soldiers[i]) {
			if (unit) {
				delete unit;
			}
		}
	}
}

Unit *Army::getSoldier(int id) {
	Unit *ret = nullptr;
	if (id > 0) {
		for (int i = frontLine; i < TOTAL_LINES; i++) {
			for (Unit *unit : soldiers[i]) {
				if (unit && unit->id == id) {
					ret = unit;
					break;
				}
			}
		}
	}

	return ret;
}

void Army::update(float ticks) {
	deaths = 0;
	arrivals = 0;
	idles = 0;

	for (int i = 0; i < TOTAL_LINES; i++) {
		for (int j = 0; j < ROWS_PER_LINE; j++) {
			Unit *unit = soldiers[i][j];
			if (!unit) {
				continue;
			}

			bool arrived = unit->arrivedTargetPosition;
			unit->update(ticks);

			if (unit->state == UnitState::dying) {
				removeUnitsTarget(unit);
			} else if (unit->state == UnitState::died) {
				releaseUnitOccupiedGrids(unit);
				releaseUnitAssignedPosition(unit);

				_DHLOG("remove unit:%d", unit->id);
				
				soldiers[i][j] = nullptr;
				delete unit;
				deaths++;
			} else if (!arrived && unit->arrivedTargetPosition) {
				arrivals++;
			} else if (unit->isStandby() && !Unit::isStandbyState(unit->previousState)) {
				idles++;
			}
		}
	}
}

void Army::updateFormationState() {
	frontLine = 0;
	while (getLineSoldierCount(frontLine) == 0) {
		frontLine++;
		if (frontLine >= TOTAL_LINES) {
			break;
		}
	}

	frontLineIndex = frontLine % LINES_PER_SQUAD;

	frontLinePosition = (faction == ArmyFaction::left ? 0.0f : FLT_MAX);

	if (frontLine < TOTAL_LINES) {
		for (Unit *unit : soldiers[frontLine]) {
			if (!unit) {
				continue;
			}

			if ((faction == ArmyFaction::left && frontLinePosition < unit->position.x) ||
					(faction == ArmyFaction::right && frontLinePosition > unit->position.x)) {
				frontLinePosition = unit->position.x;
			}
		}
	}
}

void Army::removeUnitsTarget(Unit *unit) {
	for (int i = unit->theirArmy->frontLine; i < TOTAL_LINES; ++i) {
		for (Unit *soldier : unit->theirArmy->soldiers[i]) {
			if (soldier && soldier->isAlive() && soldier->target == unit) {
				soldier->target = nullptr;
			}
		}
	}
}

int Army::getLineSoldierCount(int globalLineIndex) {
	int ret = 0;

	for (int i = 0; i < ROWS_PER_LINE; i++) {
		Unit *unit = soldiers[globalLineIndex][i];
		if (unit) {
			ret++;
		}
	}

	return ret;
}

float Army::getFrontLineTargetPosition() {
	float ret = 0;

	for (Unit *unit : soldiers[frontLine]) {
		if (unit) {
			ret = unit->targetPosition.x;
			break;
		}
	}

	return ret;
}

void Army::getFrontUnits(GridInfo info, vector<Unit *> &arr) {

}

void Army::getFrontLineUnits(vector<Unit *> &arr) {

}

void Army::getFrontSquadUnits(vector<Unit *> &arr) {
	int squadIndex = frontLine / LINES_PER_SQUAD;
	for (int i = frontLine; i < (squadIndex + 1) * LINES_PER_SQUAD; ++i) {
		for (size_t j = 0; j < ROWS_PER_LINE; ++j) {
			Unit *unit = soldiers[i][j];
			if (unit) {
				arr.push_back(unit);
			}
		}
	}
}

void Army::getSquadUnits(int squadIndex, vector<Unit *> &arr) {

}

void Army::getAllUnits(vector<Unit *> &arr) {

}

void Army::getAroundUnits(const CCPoint &pos, vector<Unit *> &arr) {

}

bool Army::isFrontSquadStandby() {
	bool ret = true;

	int squadIndex = frontLine / LINES_PER_SQUAD;
	int thirdLine = (squadIndex + 1) * LINES_PER_SQUAD - 1;

	for (int i = frontLine; i <= thirdLine; i++) {
		for (Unit *unit : soldiers[i]) {
			if (unit && !unit->isStandby()) {
				ret = false;
				break;
			}
		}
	}

	return ret;
}

bool Army::isFrontSquadAllDied() {
	bool ret = true;

	int squadIndex = frontLine / LINES_PER_SQUAD;
	int thirdLine = (squadIndex + 1) * LINES_PER_SQUAD - 1;

	for (int i = frontLine; i <= thirdLine; i++) {
		for (Unit *unit : soldiers[i]) {
			if (unit) {
				ret = false;
				break;
			}
		}
	}

	return ret;
}

void Army::march(float contactPosition) {
	float distance = contactPosition - frontLinePosition;

	if (distance > 0.0f) {
		distance -= MELEE_SPACE / 2.0f;
	} else {
		distance += MELEE_SPACE / 2.0f;
	}

	int frontSquad = frontLine / LINES_PER_SQUAD;
	for (int i = frontSquad; i < SQUAD_COUNT; i++) {
		if (i == frontSquad) {
			moveSquad(i, distance);
		} else {
			if (orders[i] != SquadOrder::hold && orders[i - 1] != SquadOrder::hold) {
				moveSquad(i, distance);
			}
		}
	}
}

void Army::moveSquad(int squadIndex, float distance) {
	int startLineIndex = squadIndex * LINES_PER_SQUAD;
	for (int i = startLineIndex; i < (startLineIndex + LINES_PER_SQUAD); i++) {
		if (i < frontLine) {
			continue;
		}

		for (Unit *unit : soldiers[i]) {
			if (unit && unit->isAlive()) {
				unit->moveTo(ccp(unit->position.x + distance, unit->position.y));
			}
		}
	}
}

Unit *Army::getFrontLineSoldier(int id) {
	Unit *ret = nullptr;

	for (Unit *unit : soldiers[frontLine]) {
		if (unit && unit->id == id) {
			ret = unit;
			break;
		}
	}

	return ret;
}

void Army::updateGridsState(Unit *unit, int line, int row) {
	if (unit->creature->genre == EquipmentGenre::artillery_3case) {
		gridsState[line][row] = unit->id;
		gridsState[line + 1][row] = unit->id;
		gridsState[line + 2][row] = unit->id;
	} else if (unit->creature->genre == EquipmentGenre::artillery_6case) {
		gridsState[line][row] = unit->id;
		gridsState[line + 1][row] = unit->id;
		gridsState[line + 2][row] = unit->id;
		gridsState[line][row + 1] = unit->id;
		gridsState[line + 1][row + 1] = unit->id;
		gridsState[line + 2][row + 1] = unit->id;
	} else if (unit->creature->genre == EquipmentGenre::artillery_9case) {
		gridsState[line][row] = unit->id;
		gridsState[line + 1][row] = unit->id;
		gridsState[line + 2][row] = unit->id;
		gridsState[line][row + 1] = unit->id;
		gridsState[line + 1][row + 1] = unit->id;
		gridsState[line + 2][row + 1] = unit->id;
		gridsState[line][row + 2] = unit->id;
		gridsState[line + 1][row + 2] = unit->id;
		gridsState[line + 2][row + 2] = unit->id;
	} else if (unit->special->genre == EquipmentGenre::special_horse || unit->special->genre == EquipmentGenre::special_boar) {
		gridsState[line][row] = unit->id;
		gridsState[line + 1][row] = unit->id;
	} else {
		gridsState[line][row] = unit->id;
	}
}

void Army::updatePositionTable(Unit *unit, int line, int row) {
	if (unit->creature->genre == EquipmentGenre::artillery_3case) {
		positionTable[line][row] = unit->id;
		positionTable[line + 1][row] = unit->id;
		positionTable[line + 2][row] = unit->id;
	} else if (unit->creature->genre == EquipmentGenre::artillery_6case) {
		positionTable[line][row] = unit->id;
		positionTable[line + 1][row] = unit->id;
		positionTable[line + 2][row] = unit->id;
		positionTable[line][row + 1] = unit->id;
		positionTable[line + 1][row + 1] = unit->id;
		positionTable[line + 2][row + 1] = unit->id;
	} else if (unit->creature->genre == EquipmentGenre::artillery_9case) {
		positionTable[line][row] = unit->id;
		positionTable[line + 1][row] = unit->id;
		positionTable[line + 2][row] = unit->id;
		positionTable[line][row + 1] = unit->id;
		positionTable[line + 1][row + 1] = unit->id;
		positionTable[line + 2][row + 1] = unit->id;
		positionTable[line][row + 2] = unit->id;
		positionTable[line + 1][row + 2] = unit->id;
		positionTable[line + 2][row + 2] = unit->id;
	} else if (unit->special->genre == EquipmentGenre::special_horse || unit->special->genre == EquipmentGenre::special_boar) {
		positionTable[line][row] = unit->id;
		positionTable[line + 1][row] = unit->id;
	} else {
		positionTable[line][row] = unit->id;
	}
}

void Army::releaseUnitOccupiedGrids(Unit *unit) {
	for (int i = 0; i < TOTAL_LINES; ++i) {
		for (int j = 0; j < ROWS_PER_LINE; ++j) {
			if (gridsState[i][j] == unit->id) {
				gridsState[i][j] = 0;
			}
		}
	}
}

void Army::releaseUnitAssignedPosition(Unit *unit) {
	for (int i = 0; i < TOTAL_LINES; ++i) {
		for (int j = 0; j < ROWS_PER_LINE; ++j) {
			if (positionTable[i][j] == unit->id) {
				positionTable[i][j] = 0;
			}
		}
	}
}

bool Army::testGridForAssigningUnit(Unit *unit, int line, int row) {
	bool ret = true;
	int squadStartLineIndex = line / LINES_PER_SQUAD * LINES_PER_SQUAD;

	vector<int> arr;
	if (unit->creature->genre == EquipmentGenre::artillery_3case) {
		if (line != squadStartLineIndex) {
			ret = false;
		} else {
			arr.push_back(positionTable[line][row]);
			arr.push_back(positionTable[line + 1][row]);
			arr.push_back(positionTable[line + 2][row]);
		}
	} else if (unit->creature->genre == EquipmentGenre::artillery_6case) {
		if (line != squadStartLineIndex || row >= ROWS_PER_LINE - 1) {
			ret = false;
		} else {
			arr.push_back(positionTable[line][row]);
			arr.push_back(positionTable[line + 1][row]);
			arr.push_back(positionTable[line + 2][row]);
			arr.push_back(positionTable[line][row + 1]);
			arr.push_back(positionTable[line + 1][row + 1]);
			arr.push_back(positionTable[line + 2][row + 1]);
		}
	} else if (unit->creature->genre == EquipmentGenre::artillery_9case) {
		if (line != squadStartLineIndex || row >= ROWS_PER_LINE - 2) {
			ret = false;
		} else {
			arr.push_back(positionTable[line][row]);
			arr.push_back(positionTable[line + 1][row]);
			arr.push_back(positionTable[line + 2][row]);
			arr.push_back(positionTable[line][row + 1]);
			arr.push_back(positionTable[line + 1][row + 1]);
			arr.push_back(positionTable[line + 2][row + 1]);
			arr.push_back(positionTable[line][row + 2]);
			arr.push_back(positionTable[line + 1][row + 2]);
			arr.push_back(positionTable[line + 2][row + 2]);
		}
	} else if (unit->special->genre == EquipmentGenre::special_horse || unit->special->genre == EquipmentGenre::special_boar) {
		if (line > squadStartLineIndex + 1) {
			ret = false;
		} else {
			arr.push_back(positionTable[line][row]);
			arr.push_back(positionTable[line + 1][row]);
		}
	} else {
		ret = !positionTable[line][row];
	}

	if(arr.size() > 0) {
		std::sort(arr.begin(), arr.end());
		arr.erase(std::unique(arr.begin(), arr.end()), arr.end());

		for(int n : arr) {
			if(n > 0 && n != unit->id) {
				ret = false;
				break;
			}
		}
	}

	return ret;
}

bool Army::testGridForMovingUnit(Unit *unit, int line, int row) {
	bool ret = true;
	int squadStartLineIndex = line / LINES_PER_SQUAD * LINES_PER_SQUAD;

	vector<int> arr;
	if (unit->creature->genre == EquipmentGenre::artillery_3case) {
		if (line != squadStartLineIndex) {
			ret = false;
		} else {
			arr.push_back(gridsState[line][row]);
			arr.push_back(gridsState[line + 1][row]);
			arr.push_back(gridsState[line + 2][row]);
		}
	} else if (unit->creature->genre == EquipmentGenre::artillery_6case) {
		if (line != squadStartLineIndex || row >= ROWS_PER_LINE - 1) {
			ret = false;
		} else {
			arr.push_back(gridsState[line][row]);
			arr.push_back(gridsState[line + 1][row]);
			arr.push_back(gridsState[line + 2][row]);
			arr.push_back(gridsState[line][row + 1]);
			arr.push_back(gridsState[line + 1][row + 1]);
			arr.push_back(gridsState[line + 2][row + 1]);
		}
	} else if (unit->creature->genre == EquipmentGenre::artillery_9case) {
		if (line != squadStartLineIndex || row >= ROWS_PER_LINE - 2) {
			ret = false;
		} else {
			arr.push_back(gridsState[line][row]);
			arr.push_back(gridsState[line + 1][row]);
			arr.push_back(gridsState[line + 2][row]);
			arr.push_back(gridsState[line][row + 1]);
			arr.push_back(gridsState[line + 1][row + 1]);
			arr.push_back(gridsState[line + 2][row + 1]);
			arr.push_back(gridsState[line][row + 2]);
			arr.push_back(gridsState[line + 1][row + 2]);
			arr.push_back(gridsState[line + 2][row + 2]);
		}
	} else if (unit->special->genre == EquipmentGenre::special_horse || unit->special->genre == EquipmentGenre::special_boar) {
		if (line > squadStartLineIndex + 1) {
			ret = false;
		} else {
			arr.push_back(gridsState[line][row]);
			arr.push_back(gridsState[line + 1][row]);
		}
	} else {
		ret = !gridsState[line][row];
	}

	if(arr.size() > 0) {
		std::sort(arr.begin(), arr.end());
		arr.erase(std::unique(arr.begin(), arr.end()), arr.end());

		for(int n : arr) {
			if(n > 0 && n != unit->id) {
				ret = false;
				break;
			}
		}
	}

	return ret;
}

Unit *Army::getSoldierForAssign(int line, int row) {
	Unit *ret = nullptr;
	Unit* unit = getSoldier(line, row);
	if (unit && !unit->target && unit->isAlive() && !unit->isAssigning) {
		if (unit->creature->genre == EquipmentGenre::artillery_3case ||
			unit->creature->genre == EquipmentGenre::artillery_6case ||
			unit->creature->genre == EquipmentGenre::artillery_9case ||
			unit->special->genre == EquipmentGenre::special_horse ||
			unit->special->genre == EquipmentGenre::special_boar) {
			if(line == unit->gridInfo.globalLineIndex) {
				ret = unit;
			}
		} else {
			ret = unit;
		}
	}

	return ret;
}
